Skip to content

Add riscv64 (RV64GC) Linux support - #495

Open
Opvolger wants to merge 1 commit into
nillerusr:masterfrom
Opvolger:feature/riscv-support
Open

Opvolger wants to merge 1 commit into
nillerusr:masterfrom
Opvolger:feature/riscv-support

Conversation

@Opvolger

@Opvolger Opvolger commented Nov 7, 2026

Copy link
Copy Markdown

This adds support for 64-bit RISC-V Linux (RV64GC, so no vector extension needed). Half-Life 2 builds, runs and renders correctly on two RISC-V boards with an AMD GPU.

The approach follows the existing ARM support: wherever the code has an __arm__ / __aarch64__ case, riscv64 takes the same non-x86 path. For SSE, SIMDe is used in the same way that sse2neon.h is used on ARM.

How to build

On a riscv64 machine running Debian 13 (trixie):

scripts/build-debian-riscv64.sh

This works like the other scripts/build-*.sh scripts. The only new dependency is libsimde-dev (SIMDe headers; on Fedora simde-devel).

Changes

  • public/tier0/platform.h: new PLATFORM_RISCV64, handled next to ARM for PLATFORM_64BITS, DebuggerBreak, the FPU control word (no-op), little-endian and Plat_Rdtsc (clock_gettime).
  • SIMD: new common/sse2simde.h, the RISC-V counterpart of sse2neon.h. It enables SIMDe's native aliases, so the existing _mm_* SSE code compiles unchanged. It is included from ssemath.h, vector4d.h and mathlib/sse.cpp. sse.cpp takes the same non-inline-asm paths as ARM.
  • public/mathlib/mathlib.h: RoundFloatToInt and RoundFloatToUnsignedLong use lrintf / llrintf (round to nearest even, like cvtss2si on x86).
  • public/materialsystem/imesh.h: the intrinsics paths instead of the x86 MMX inline asm.
  • public/tier0/tslist.h: the lock-free list needs a 128-bit CAS. RISC-V has no such instruction (no Zacas), so __atomic_compare_exchange_n is used, which libatomic provides (lock-based).
  • tier0/cpu.cpp, tier0/cpu_posix.cpp, tier1/processor_detect_linux.cpp, tier1/reliabletimer.cpp, tier1/pathmatch.cpp, public/saverestoretypes.h, public/steam/steamtypes.h: no x86 cpuid, rdtsc or int3 on riscv. The arch name is riscv64 and the vendor RISC-V.
  • wscript: -fsigned-char for riscv (char is unsigned there, like on ARM, and the code assumes x86). It also links libatomic, as LIB rather than a link flag, so it comes after the objects with --as-needed.
  • scripts/build-debian-riscv64.sh: native build script for Debian 13.

Testing

Both boards run Debian 13 with an AMD Radeon R9 290 (radeonsi, desktop OpenGL) on PCIe and KDE Plasma. The test was a timedemo on a d1_canals_01 demo (5449 frames, 1280×720):

Board SoC Build Result
Milk-V Jupiter SpacemiT K1 (8 cores) release ~52 fps
StarFive VisionFive 2 JH7110 (4× U74) debug (from build-debian-riscv64.sh) 27 fps

The x86_64 build was checked again with these changes: it still builds and runs.

Under Wayland, start the game with SDL_VIDEODRIVER=x11 until #493 is merged. That crash is not RISC-V specific.

Not included

  • No -march flags: the default rv64gc works on every RV64GC board. I also tried an RVV (vector) build on the Jupiter. It was not faster, because the game is limited by the main thread, so I left it out to keep this PR small.

How this was made

I made this together with Claude Code, Anthropic's AI coding assistant. I guided it throughout and corrected it where needed, so that the change fits the existing project structure:

  • the same non-x86 paths as ARM
  • a SIMDe header next to sse2neon.h
  • the same kind of build script as the other scripts/build-*.sh, with no extra container or cross-compile tooling

I tested the result myself on both boards listed above.

Tested on a Milk-V Jupiter (SpacemiT K1) and a StarFive VisionFive 2
(JH7110), both running Debian 13 with an AMD R9 290 (radeonsi):
Half-Life 2 runs and renders correctly.

- platform.h: add PLATFORM_RISCV64 and handle it next to ARM
  (PLATFORM_64BITS, DebuggerBreak, FPU control word, endianness,
  Plat_Rdtsc via clock_gettime).
- SIMD: add common/sse2simde.h, which maps the SSE intrinsics to SIMDe
  (the counterpart of sse2neon.h on ARM); used by ssemath.h, vector4d.h
  and mathlib/sse.cpp. sse.cpp takes the non-asm paths.
- mathlib.h: RoundFloatToInt/RoundFloatToUnsignedLong via lrintf/llrintf
  (round to nearest even, like cvtss2si on x86).
- imesh.h: use the intrinsics paths instead of x86 MMX inline asm.
- tslist.h: RISC-V has no 128-bit CAS instruction (no Zacas), so use
  __atomic_compare_exchange_n, provided (lock-based) by libatomic.
- cpu.cpp, cpu_posix.cpp, processor_detect_linux.cpp, reliabletimer.cpp,
  pathmatch.cpp, saverestoretypes.h, steamtypes.h: no x86 cpuid/rdtsc/
  int3 on riscv.
- wscript: -fsigned-char for riscv (char is unsigned by default) and link
  libatomic (as LIB, so it is placed after the objects with --as-needed).
- scripts/build-debian-riscv64.sh: native build on riscv64 Debian 13
  (trixie), like the other build-*.sh scripts; also installs libsimde-dev.

Co-Authored-By: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants